package com.sdp.right.operlog;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.sdp.core.bizc.annotation.OperLog;
import com.sdp.core.bizc.threadLocal.ContextManager;
import com.sdp.core.bizc.utils.CoreConstantsEnum;
import com.sdp.right.db.sys_emp.service.ISysEmpService;
import com.sdp.right.db.sys_oper_log.service.ISysOperLogService;
import com.sdp.right.entity.SysEmp;
import com.sdp.right.entity.SysOperLog;

@Aspect
@Component
@Transactional(propagation=Propagation.REQUIRES_NEW)
@DS(value="right")
public class OperLogAspect {
	
	@Autowired
	private ISysOperLogService iSysOperLogService;
	
	@Autowired
	private ISysEmpService iSysEmpService;
	
	/**
	* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
	*/
	@Pointcut("@annotation(com.sdp.core.bizc.annotation.OperLog)")
	public void operLogPoinCut() {
	}

	@AfterReturning(value = "operLogPoinCut()", returning = "keys")
	public void saveOperLog(JoinPoint joinPoint, Object keys) {
		// 获取RequestAttributes
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

		// 从获取RequestAttributes中获取HttpServletRequest的信息
		HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

		try {
			SysOperLog sysOperLog = new SysOperLog();
			
			// 从切面织入点处通过反射机制获取织入点处的方法
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			// 获取切入点所在的方法
			Method method = signature.getMethod();
			OperLog opLog = method.getAnnotation(OperLog.class);
			if (opLog != null) {
				sysOperLog.setOperDes(opLog.operDesc());
				sysOperLog.setSystemNo(opLog.operSystemNo());
			}
			// 获取请求的类名
			String className = joinPoint.getTarget().getClass().getName();
			// 获取请求的方法名
			String methodName = method.getName();
			methodName = className + "." + methodName;
			
			SysEmp emp = iSysEmpService.querySysEmpByAccountNo(ContextManager.getContextData(CoreConstantsEnum.token中账号的属性名.getCode()));
			sysOperLog.setOperEmpName(emp.getEmpName());
			sysOperLog.setOperEmpNo(emp.getEmpNo());
			sysOperLog.setOperAccountNo(emp.getAccountNo());
			sysOperLog.setOperIp(getIpAddress(request));
			sysOperLog.setOperMethod(methodName);
			sysOperLog.setOperRequParam(JSON.toJSONString(getFieldsName(joinPoint)));
			sysOperLog.setOperRespParam(JSON.toJSONString(keys));
			sysOperLog.setOperTime(LocalDateTime.now());
			sysOperLog.setOperUrl(request.getRequestURI());
			
			iSysOperLogService.save(sysOperLog);

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}


	@SuppressWarnings("rawtypes")
	private static Map getFieldsName(JoinPoint joinPoint) throws ClassNotFoundException, NoSuchMethodException {
        String classType = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        // 参数值
        Object[] args = joinPoint.getArgs();
        Class<?>[] classes = new Class[args.length];
        for (int k = 0; k < args.length; k++) {
            if (!args[k].getClass().isPrimitive()) {
                // 获取的是封装类型而不是基础类型
                String result = args[k].getClass().getName();
                Class s = map.get(result);
                classes[k] = s == null ? args[k].getClass() : s;
            }
        }
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        // 获取指定的方法，第二个参数可以不传，但是为了防止有重载的现象，还是需要传入参数的类型
        Method method = Class.forName(classType).getMethod(methodName, classes);
        // 参数名
        String[] parameterNames = pnd.getParameterNames(method);
        // 通过map封装参数和参数值
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }
	
	@SuppressWarnings("rawtypes")
	private static HashMap<String, Class> map = new HashMap<String, Class>() {
   
		private static final long serialVersionUID = 1L;

		{
            put("java.lang.Integer", int.class);
            put("java.lang.Double", double.class);
            put("java.lang.Float", float.class);
            put("java.lang.Long", long.class);
            put("java.lang.Short", short.class);
            put("java.lang.Boolean", boolean.class);
            put("java.lang.Char", char.class);
        }
    };

    /**
     * 获取真实IP
     * @param request
     * @return
     */
	private static String getIpAddress(HttpServletRequest request) {
        String Xip = request.getHeader("X-Real-IP");
        String XFor = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = XFor.indexOf(",");
            if(index != -1){
                return XFor.substring(0,index);
            }else{
                return XFor;
            }
        }
        XFor = Xip;
        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
            return XFor;
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getRemoteAddr();
        }
        return XFor;
    }


}
